/**
 * 
 */
package it.unitn.testbed.client.panels;

import gwtupload.client.IUploader;
import gwtupload.client.MultiUploader;
import gwtupload.client.SingleUploader;
import gwtupload.client.IUploadStatus.Status;
import it.unitn.testbed.client.Testbed;
import it.unitn.testbed.client.TestbedService;
import it.unitn.testbed.client.common.BriefTaskComponent;
import it.unitn.testbed.client.common.MoteConfig;

import java.util.Date;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * The task panel, where users can submit tasks on the testbed.
 * 
 * @author Stefan Guna
 * 
 */
public class TaskPanel extends FlowPanel {

	/** Callback for {@link TestbedService#getTestbedConfig()} */
	private class ConfigCallback implements AsyncCallback<MoteConfig[]> {
		public void onFailure(Throwable caught) {
			testbed.setStatus("Error refreshing testbed: "
					+ caught.getMessage());
		}

		public void onSuccess(MoteConfig[] result) {
			config = result;
			addMotesToForm();
		}
	}

	/** Handler for the button that removes task components. */
	private class RemoveHandler implements ClickHandler {
		private long id;

		public RemoveHandler(long id) {
			this.id = id;
		}

		public void onClick(ClickEvent event) {
			testbed.getTestbedService().removeTaskComponent(id,
					new TaskCallback());
		}
	}

	/** Handler for serial message classes uploader. */
	private class SerialMsgUploadFinish implements
			IUploader.OnFinishUploaderHandler {
		public void onFinish(IUploader uploader) {
			if (uploader.getStatus() == Status.SUCCESS)
				testbed.setStatus(uploader.getFileName()
						+ " uploaded successfully.");
			if (uploader.getStatus() == Status.ERROR)
				testbed.setStatus("Error in uploading "
						+ uploader.getFileName() + ": "
						+ uploader.getServerResponse());
			if (uploader.getStatus() == Status.DELETED)
				testbed.setStatus(uploader.getFileName() + " removed.");
		}
	}

	/** Callback for {@link TestbedService#getTaskComponents()} */
	private class TaskCallback implements AsyncCallback<BriefTaskComponent[]> {
		public void onFailure(Throwable caught) {
			testbed.setStatus("Error refreshing task: " + caught.getMessage());
			taskTable.setVisible(false);
			taskSubmit.setVisible(false);
		}

		public void onSuccess(BriefTaskComponent[] result) {
			while (taskTable.getRowCount() > 0)
				taskTable.removeRow(0);

			taskTable.setVisible(true);
			int row = 1;
			taskTable.setText(0, 0, "filename");
			taskTable.setText(0, 1, "motes");
			for (BriefTaskComponent comp : result) {
				taskTable.setText(row, 0, comp.getFilename());
				taskTable.setText(row, 1, comp.getMoteIds().toString());
				taskTable.setWidget(row, 2, new Button("x", new RemoveHandler(
						comp.getId())));
				row++;
			}
			if (row > 1)
				taskSubmit.setVisible(true);
			else
				taskSubmit.setVisible(false);
		}
	}

	/** Handler for the form that uploads a task component. */
	private class TaskUploadFinish implements IUploader.OnFinishUploaderHandler {
		public void onFinish(IUploader uploader) {
			if (uploader.getStatus() == Status.SUCCESS) {
				testbed.setStatus(uploader.getFileName()
						+ " uploaded successfully.");
				testbed.getTestbedService().getTaskComponents(
						new TaskCallback());
			}
			if (uploader.getStatus() == Status.ERROR) {
				testbed.setStatus("Error in uploading "
						+ uploader.getFileName() + ": "
						+ uploader.getServerResponse());
			}
		}
	}

	/**
	 * Keeps track of how many motes have been checked to be assigned for the
	 * current mote binary.
	 */
	private int checkedCount = 0;
	private MoteConfig[] config;
	/** Holds checkboxes with available motes. */
	private FlowPanel motesPanel;
	private MultiUploader serialMsgUploader;
	private TextBox taskDuration;
	private FlowPanel taskSubmit;
	private FlexTable taskTable;
	private Testbed testbed;
	/** Button for uploading a mote binary. */
	private Button uploadButton;

	public TaskPanel(final Testbed testbed) {
		this.testbed = testbed;

		/* Build the binary - motes select form */
		final VerticalPanel binFormHolder = new VerticalPanel();
		uploadButton = new Button("Upload image");
		uploadButton.setVisible(false);
		motesPanel = new FlowPanel();
		binFormHolder.add(new Label("Select binary image:"));

		FormPanel binForm = new FormPanel() {
			{
				super.add(binFormHolder);
			}

			public void add(Widget w) {
				binFormHolder.add(w);
			}
		};

		SingleUploader taskUploader = new SingleUploader(null, uploadButton,
				binForm);
		taskUploader.setServletPath("send.task");
		String validExtensions[] = new String[1];

		validExtensions[0] = "exe";
		taskUploader.setValidExtensions(validExtensions);
		taskUploader.addOnFinishUploadHandler(new TaskUploadFinish());

		binFormHolder.add(uploadButton);
		binFormHolder.add(new Label("Select motes:"));
		binFormHolder.add(motesPanel);

		// TODO make it an event on click or on diplay
		testbed.getTestbedService().getTestbedConfig(new ConfigCallback());

		/* Build the table holding mappins bin image - mote ids */
		taskTable = new FlexTable();

		/* Build the task submit area */
		taskSubmit = new FlowPanel();
		taskDuration = new TextBox();

		taskSubmit.add(new Label("Task duration (minutes):"));
		taskSubmit.add(taskDuration);

		taskSubmit.add(new Button("Submit task", new ClickHandler() {
			public void onClick(ClickEvent event) {
				int duration;
				try {
					duration = Integer.parseInt(taskDuration.getValue());
				} catch (NumberFormatException e) {
					Window.alert("Invalid task duration!");
					return;
				}
				if (duration < 1) {
					Window.alert("Invalid task duration!");
					return;
				}

				testbed.getTestbedService().submitTask(duration,
						new AsyncCallback<Date>() {
							public void onFailure(Throwable caught) {
								testbed.newTaskPanel(true);
								taskTable.setVisible(false);
								taskSubmit.setVisible(false);
								testbed.setStatus("Unable to schedule task "
										+ caught.getMessage());
							}

							public void onSuccess(Date date) {
								testbed.newTaskPanel(true);
								taskTable.setVisible(false);
								taskSubmit.setVisible(false);
								testbed.setStatus("Task will execute at "
										+ date);
							}
						});
			}
		}));
		taskSubmit.setVisible(false);

		/* Build the area where serial messages are uploaded */
		final VerticalPanel serialMsgHolder = new VerticalPanel();
		serialMsgHolder.add(new Label("Upload serial message classes:"));

		serialMsgUploader = new MultiUploader();

		serialMsgUploader.setServletPath("send.serial");
		validExtensions = new String[1];

		validExtensions[0] = "class";
		serialMsgUploader.setValidExtensions(validExtensions);
		serialMsgUploader.addOnFinishUploadHandler(new SerialMsgUploadFinish());

		serialMsgHolder.add(serialMsgUploader);

		/* Display all these */
		add(binForm);
		add(serialMsgHolder);
		add(taskTable);
		add(taskSubmit);
	}

	void addMotesToForm() {
		while (motesPanel.getWidgetCount() > 0)
			motesPanel.remove(0);

		final CheckBox all = new CheckBox("ALL");

		for (MoteConfig mote : config) {
			CheckBox checkbox = new CheckBox(mote.getId().toString());
			checkbox.setName(mote.getId().toString());
			checkbox.addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					boolean checked = ((CheckBox) event.getSource()).getValue();
					if (!checked) {
						all.setValue(false);
						checkedCount--;
					} else
						checkedCount++;
					if (checkedCount > 0)
						uploadButton.setVisible(true);
					else
						uploadButton.setVisible(false);
				}
			});
			motesPanel.add(checkbox);
		}

		all.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				boolean checked = ((CheckBox) event.getSource()).getValue();
				int cnt = 0;
				for (int i = 0; i < motesPanel.getWidgetCount(); i++) {
					Widget w = motesPanel.getWidget(i);
					if (!(w instanceof CheckBox) || w.equals(all))
						continue;
					CheckBox box = (CheckBox) w;
					cnt++;
					if (checked)
						box.setValue(true);
				}
				if (checked)
					checkedCount = cnt;
				if (checkedCount > 0)
					uploadButton.setVisible(true);
				else
					uploadButton.setVisible(false);
			}
		});

		motesPanel.add(all);
		testbed.getTestbedService().newTask(null);
	}
}
